home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Pascal Super Library
/
Pascal Super Library (CW International)(1997).bin
/
LIBRARY
/
PBLIB1
/
UNITS
/
PBXBASE.PAS
< prev
Wrap
Pascal/Delphi Source File
|
1994-05-03
|
25KB
|
805 lines
{SECTION ..PbXBASE }
UNIT PbXBASE;
INTERFACE
uses DOS, PbMISC;
{
Description: XBASE File Object(s)
Author : Howard Richoux
Date : 11/24/93
Last revised: 11/24/93
2/18/94 new libraries
Application : IBM PC and compatibles, done in Turbo Pascal 7.0
Status : Placed in the Public Domain by HNR Software 1/29/1994
Published in: none
Loosely based on DBLKSTUF
}
{SECTION .DEFS }
const BOF = -1; { Beginning of .DBF file. }
DBOK = 0; { No errors. }
EOF = 1; { End of DBF file. }
READ_ERR = 2; { Blockread error }
CLOSE_ERR = 3; { Error closing .DBF file }
REWRITE_ERR = -2;
POSITION_ERR = -3;
dbREADONLY = true;
dbREADWRITE = false;
{*** modified 2/10/94 - original is on \hnrold\PbXBASE.sav }
type rdef_type = record { Dbase record definitions we use }
name :string[10];
rtype :char; { type of record - C,N,D,L,etc. }
width :byte; { total field width of this record }
decp :byte; { number of digits to right of decimal }
stloc :integer; { offset from start of field where this }
end;
type db4head_type = record { Dbase III + header definition }
dbvno :byte; { version number (03h or 83h ) }
updyr :byte; { last update YY MM DD }
updmo :byte;
upddy :byte;
no_rec :longint; { number of record in database }
header_bytes :integer; { number of bytes in header }
rec_bytes :integer; { number of bytes in records }
tmp :array[1..20] of char; { reserved bytes in header }
end;
type db4ref_type = record { Actual header field def record }
name :array[1..11] of char; { Name of this record }
rtype :char; { type of record - C,N,D,L,etc. }
fld_addr :longint; { not used }
width :byte; { total field width of this record }
decp :byte; { number of digits to right of decimal }
multi_user :integer; { reserved for multi user }
work_id :byte; { Work area ID }
m_user :integer; { reserved for multi_user }
set_fields :byte; { SET_FIELDS flag }
resrvd :array[1..8] of byte; { 8 bytes reserved }
end; { record starts }
{-}
{SECTION .XBASE_DBF_object }
{PAGE}
const bufmax = 2048; { DBASE Spec is 4096 }
TYPE XBASE_DBF_object = OBJECT
opened : boolean; { is this file open?}
writepermitted : boolean; { based on open mode }
dbbuf : array[1..bufmax] of char; { Dbase record }
dbhead : db4head_type; { header of DBF file }
rstru : array[1..50] of rdef_type; { version of the rec structure }
no_col : integer; { number of columns in database }
dbfin : file;
rec_stru : db4ref_type; { actual database rec structure }
infile : string; { name of database }
db_rec_no: longint; { Present record of DBF file }
err : integer;
Procedure Init(dbfilename:string; readonly : boolean);
Procedure done;
Function NoError : boolean;
Procedure dbshowstruc;
Procedure dblistrecs; {lists all records in SDF format }
Function dbclose:boolean; {closes dbase file }
Function dbfldno(fname:string):integer; {Field name -> Field Number }
Function dbfldname(fnum:integer):string; {Field number -> Field Name }
Function dbfldrtype(fnum:integer):char;
Function dbfldwidth(fnum:integer):integer;
Function dbflddecp (fnum:integer):integer;
Function dbnumfields : integer;
Function dbrecsize : integer;
Function dbnumrecs : integer;
Procedure dbcleardbbuf;
Function dbstr(fldno:integer):string; {Fetches string value of field }
Function dbint(fldno:integer):integer; {Fetches integer value of field }
Function dblong(fldno:integer):longint; {Fetches longint value of field }
Function dbreal(fldno:integer):real; {Fetches real value of field }
Function dblogic(fldno:integer):boolean; {Fetches boolean value of field }
Function dbdeleted:boolean; {Returns true if current record is deleted }
Function dbrecno:longint; {Returns current record number }
Function dbposition(rec_no:longint):boolean; {does the work}
Function dbgoto(rec_no:longint):boolean; {Goto record rec_no }
Function dbskip(rec_no:longint):boolean; {Move forward and read next }
Function dbtop:boolean; {Move to record 1 and read }
Function dbbottom:boolean; {Move to last record and read }
Procedure dbputstr(fldno:integer; s : string);
Procedure dbputdate(fldno:integer; s : string);
Procedure dbputint(fldno:integer; x : integer);
Procedure dbputlong(fldno:integer; x : longint);
Procedure dbputreal(fldno:integer; x : real);
Function dbrewrite(rec_no:longint):boolean;
Function dbdelete(rec_no:longint):boolean;
Function dbappend :boolean;
Function dbExportrec : string;
Function dbExportDef : string;
Procedure dbFieldInfo(fldno:integer; var fldnam:string; var rtype:char;
var width,decp : byte);
{private methods}
Procedure calc_coloff;
Procedure dbSetHeaderDate;
Function dbUpdateHeader :boolean;
end;
{+}
{SECTION .zzImplementation }
IMPLEMENTATION
{SECTION XBASE_DBF_object }
Procedure XBASE_DBF_object.Init(dbfilename : string; readonly : boolean);
var numread :word;
i,j,errnull :integer;
begin
writepermitted := false;
opened := false;
err := 0;
infile := dbfilename; { save filename }
if readonly then FileMode := 0
else begin
FileMode := 2;
writepermitted := true;
end;
{ ForceExt(infile,'dbf');}
assign(dbfin,infile);
{$I-}
reset(dbfin,1); { record size to read = 1 }
{$I+}
err := IOResult;
if err <> 0 then exit;
{$I-}
blockread(dbfin,dbhead,sizeof(dbhead),numread);
{$I+}
err := IOResult;
if err <> 0 then exit;
if dbhead.rec_bytes > bufmax then
begin
err := -50;
writeln('***DBF rec size too large, I am allowing bufmax=',bufmax,' bytes.');
writeln(' This record is: ',dbhead.rec_bytes,' bytes.');
writeln(' To handle this, PbXBASE must be changed.');
end;
if(numread = 0) then err := READ_ERR
else begin { calc the number of cols of data to read, put in global }
no_col := ((dbhead.header_bytes - sizeof(dbhead)) div 32);
{ writeln('field calcs ',no_col,' ',dbhead.header_bytes,' ',
sizeof(dbhead)); }
for i := 1 to no_col do { read the column definitions }
begin
{$I-}
blockread(dbfin,rec_stru,sizeof(rec_stru),numread);
{$I+}
err := IOResult;
if err <> 0 then exit;
if(numread = 0) then err := READ_ERR
else begin { move it to users structure }
rstru[i].rtype := rec_stru.rtype;
rstru[i].width := rec_stru.width;
rstru[i].decp := rec_stru.decp;
j := 1; { convert from C string to Pascal string }
while((ord(rec_stru.name[j]) > 0) and (j <= 10)) do
begin
rstru[i].name[j] := rec_stru.name[j];
inc(j);
end;
rstru[i].name[0] := chr(lo(j-1)); { set string length }
end;
end;
calc_coloff; { calculate column offsets }
dbgoto(1); { ignore error }
err := 0;
end;
if err <> 0 then
begin
writeln('Init - error ',err);
end
else opened := true;
end;
Function XBASE_DBF_object.NoError : boolean;
begin
NoError := (Err = 0);
end;
Function XBASE_DBF_object.dbclose : boolean;
{ Call at end of your application to close the Dbase file. For now
there is only one file to close, if extended to use
multiple database files then this procedure would be required.
Returns STD_ERR_CODES.}
begin
err := 0;
dbclose := false;
if opened then
begin
{$I-} close(dbfin); {$I-}
err := IOResult;
end
else err := -999; {file not open}
dbclose := NoError;
end;
Procedure XBASE_DBF_object.done;
begin
if not dbclose then writeln('Done - Close error ',err);
end;
Function XBASE_DBF_object.dbfldname( fnum:integer ):string;
begin
if (fnum > 0) and (fnum <= no_col) then
dbfldname := rstru[fnum].name
else dbfldname := '';
end;
Function XBASE_DBF_object.dbfldrtype(fnum:integer):char;
begin
if (fnum > 0) and (fnum <= no_col) then
dbfldrtype := rstru[fnum].rtype
else dbfldrtype := '?';
end;
Function XBASE_DBF_object.dbfldwidth(fnum:integer) : integer;
begin
if (fnum > 0) and (fnum <= no_col) then
dbfldwidth := rstru[fnum].width
else dbfldwidth := 1;
end;
Function XBASE_DBF_object.dbflddecp(fnum:integer) : integer;
begin
if (fnum > 0) and (fnum <= no_col) then
dbflddecp := rstru[fnum].decp
else dbflddecp := 0;
end;
Function XBASE_DBF_object.dbnumfields : integer;
begin
dbnumfields := no_col;
end;
Function XBASE_DBF_object.dbrecsize : integer;
begin
dbrecsize := dbhead.rec_bytes;
end;
Function XBASE_DBF_object.dbnumrecs : integer;
begin
dbnumrecs := dbhead.no_rec;
end;
Procedure XBASE_DBF_object.dbcleardbbuf;
begin
fillchar(dbbuf,sizeof(dbbuf),0);
end;
Function XBASE_DBF_object.dbfldno(fname:string):integer;
{ Returns an integer which is the number in the rstru array where fname
is located. Used to enable user to use field names in Functions to
return data. Returns 0 if fname not found.}
var i :integer;
begin
dbfldno := 0; { default to not found }
for i := 1 to no_col do if(fname = rstru[i].name) then dbfldno := i;
end;
Procedure XBASE_DBF_object.dbshowstruc;
var i :integer;
tmp :string[20];
tpe :string[10];
begin
err := 0;
writeln('Structure for database :',infile);
with dbhead do
begin
writeln('Date of last update :',updmo:2,'/',upddy:2,'/',updyr:2);
writeln('Number of records :',no_rec:8);
writeln('Column Type Width Decimals Offset');
writeln('---------- ---------- ------ -------- ------');
writeln(' Delete Flg 1 1');
end;
for i := 1 to no_col do
begin
with rstru[i] do
begin
tmp := copy(concat(rstru[i].name,' '),1,10);
case rtype of
'C' :tpe := 'Character';
'N' :tpe := 'Numeric ';
'D' :tpe := 'Date ';
'L' :tpe := 'Logical ';
'M' :tpe := 'Memo ';
else tpe := 'Unknown ';
end;
writeln(tmp,' ',tpe,' ',width:4,' ',
decp:3,' ',rstru[i].stloc:4);
end;
end;
writeln;
writeln(' Record length -> ',dbhead.rec_bytes:4);
end;
Procedure XBASE_DBF_object.calc_coloff; { calculate the offset from the beginning of
the record for each data element.}
var i,j :integer;
begin
j := 2; { first element of record is deleted flag }
for i := 1 to no_col do
begin
with rstru[i] do
begin
stloc := j;
j := j + width;
end; {with}
end; {for}
end; {Procedure calc_coloff}
Function XBASE_DBF_object.dbposition(rec_no:longint):boolean;
var fileloc :longint;
begin
err := 0;
dbposition := false;
if(rec_no < 1) then
begin
dbposition := true;
rec_no := 1;
end;
if(rec_no > dbhead.no_rec) then
begin
err := POSITION_ERR;
dbposition := false;
rec_no := dbhead.no_rec;
end;
db_rec_no := rec_no;
fileloc := (dbhead.header_bytes + ((rec_no -1) * dbhead.rec_bytes));
{$I-} seek(dbfin,fileloc); {$I+}
err := IOResult;
dbposition := NoError;
end;
Function XBASE_DBF_object.dbgoto(rec_no:longint):boolean;
var numread :word;
fileloc :longint;
begin
err := 0;
dbgoto := false;
if rec_no > dbhead.no_rec then
begin
err := POSITION_err;
end
else begin
if dbposition(rec_no) then
begin
{$I-} blockread(dbfin,dbbuf,dbhead.rec_bytes,numread); {$I+}
err := IOResult;
if(numread = 0) then err := READ_ERR;
end
else err := READ_ERR;
end;
dbgoto := NoError;
end;
Procedure XBASE_DBF_object.dblistrecs; { list all records in the file }
var tmp_recno :longint;
numread :word;
j :integer;
begin
err := 0;
if not opened then exit;
{$I-} seek(dbfin,dbhead.header_bytes); { position to first record } {$I+}
err := IOResult;
if err <> 0 then exit;
{ file is already open and positioned to the first data record }
tmp_recno := dbhead.no_rec;
while (tmp_recno > 0) do { need a while loop for more than int }
begin
{$I-} blockread(dbfin,dbbuf,dbhead.rec_bytes,numread); {$I+}
err := IOResult;
if(numread > 0) then
begin
write('!');
for j := 1 to dbhead.rec_bytes do write(dbbuf[j]);
writeln('!');
dec(tmp_recno);
end;
end;
end;
Function XBASE_DBF_object.dbstr(fldno:integer):string;
var tmp :string;
i :integer;
begin
for i := 1 to rstru[fldno].width do
tmp[i] := dbbuf[rstru[fldno].stloc + i - 1];
tmp[0] := chr(rstru[fldno].width);
dbstr := tmp;
end;
Function XBASE_DBF_object.dbint(fldno:integer):integer;
var tmp :string;
i,result :integer;
begin
for i := 1 to rstru[fldno].width do
tmp[i] := dbbuf[rstru[fldno].stloc + i - 1];
tmp[0] := chr(rstru[fldno].width);
val(tmp,i,result);
dbint := i;
end;
Function XBASE_DBF_object.dblong(fldno:integer):longint;
var tmp :string;
i,result :integer;
retval :longint;
begin
for i := 1 to rstru[fldno].width do
tmp[i] := dbbuf[rstru[fldno].stloc + i - 1];
tmp[0] := chr(rstru[fldno].width);
val(tmp,retval,result);
dblong := retval;
end;
Function XBASE_DBF_object.dbreal(fldno:integer):real;
var tmp :string;
i,result :integer;
retval :real;
begin
for i := 1 to rstru[fldno].width do
tmp[i] := dbbuf[rstru[fldno].stloc + i - 1];
tmp[0] := chr(rstru[fldno].width);
val(tmp,retval,result);
dbreal := retval;
end;
Function XBASE_DBF_object.dblogic(fldno:integer):boolean;
var i :integer;
begin
i := rstru[fldno].stloc;
if((dbbuf[i] = 'T') or (dbbuf[i] = 't') or (dbbuf[i] = 'Y') or
(dbbuf[i] = 'y')) then
dblogic := true
else dblogic := false;
end;
Function XBASE_DBF_object.dbdeleted:boolean;
begin
err := 0;
dbdeleted := false;
if(dbbuf[1] = '*') then
dbdeleted := true
else dbdeleted := false;
end;
Function XBASE_DBF_object.dbrecno:longint;
{ Returns the present record number in the database. }
begin
dbrecno := db_rec_no;
end;
Function XBASE_DBF_object.dbskip(rec_no:longint):boolean;
{ positions .DBF file forward (+rec_no) or backwards (-rec_no) rec_no
records from present position. Fills dbbuf[] from new DBF record.
Returns STD_ERR_CODES.
}
begin
err := 0;
dbskip := false;
if(rec_no > 0) then inc(db_rec_no,rec_no);
if(rec_no < 0) then dec(db_rec_no,rec_no);
dbskip := dbgoto(db_rec_no);
end;
Function XBASE_DBF_object.dbtop:boolean;
{ Positions .DBF file to record 1, fills dbbuf[] with data }
begin
err := 0;
dbtop := false;
dbtop := dbgoto(1);
end;
Function XBASE_DBF_object.dbbottom:boolean;
{ Positions .DBF file to last record, fills dbbuf[] with data }
begin
err := 0;
dbbottom := false;
dbbottom := dbgoto(dbhead.no_rec);
end;
{PAGE}
{ ************ Write support *****************************************}
Procedure XBASE_DBF_object.dbputstr(fldno:integer; s : string);
{ Places the string into any field of the database. This
field is filled out to the full field length by padding with spaces.
}
var i,j :integer;
begin
for i := 1 to rstru[fldno].width do
dbbuf[rstru[fldno].stloc + i - 1] := ' ';
j := min(length(s),rstru[fldno].width);
if j > 0 then
begin
for i := 1 to j do
begin
dbbuf[rstru[fldno].stloc + i - 1] := s[i];
end;
end;
end;
Procedure XBASE_DBF_object.dbputdate(fldno:integer; s : string);
{ Date comes in as a 8 character string "yyyymmdd"}
var i,j :integer;
begin
for i := 1 to rstru[fldno].width do
dbbuf[rstru[fldno].stloc + i - 1] := '0';
j := min(length(s),rstru[fldno].width);
if j > 0 then
begin
for i := 1 to j do dbbuf[rstru[fldno].stloc + i - 1] := s[i];
end;
end; {Function dbputdate}
Procedure XBASE_DBF_object.dbputint(fldno:integer; x : integer);
var i,j,k :integer;
s : string;
begin
for i := 1 to rstru[fldno].width do
dbbuf[rstru[fldno].stloc + i - 1] := ' ';
j := rstru[fldno].width;
s := integerstr(x,j);
for i := 1 to j do dbbuf[rstru[fldno].stloc + i - 1] := s[i];
end;
Procedure XBASE_DBF_object.dbputlong(fldno:integer; x : longint);
var i,j,k :integer;
s : string;
begin
for i := 1 to rstru[fldno].width do
dbbuf[rstru[fldno].stloc + i - 1] := ' ';
j := rstru[fldno].width;
s := longintstr(x,j);
for i := 1 to j do dbbuf[rstru[fldno].stloc + i - 1] := s[i];
end;
Procedure XBASE_DBF_object.dbputreal(fldno:integer; x : real);
var i,j,k :integer;
s : string;
begin
for i := 1 to rstru[fldno].width do
dbbuf[rstru[fldno].stloc + i - 1] := ' ';
j := rstru[fldno].width;
k := rstru[fldno].decp;
s := realstr(x,j,k);
for i := 1 to j do dbbuf[rstru[fldno].stloc + i - 1] := s[i];
end;
Procedure XBASE_DBF_object.dbSetHeaderDate;
var year, month, day, doy : word;
begin
GetDate(year,month,day,doy);
dbhead.updyr := byte(year-1900);
dbhead.updmo := byte(month);
dbhead.upddy := byte(day);
end;
Function XBASE_DBF_object.dbUpdateHeader : boolean;
{ rewrites the first portion of the file header,
returns STD_ERR_CODES.}
var numwritten :word;
fileloc :longint;
begin
err := 0;
dbUpdateHeader := false;
if not opened or not writepermitted then
begin
err := -99;
exit;
end;
fileloc := 0;
{$I-} seek(dbfin,fileloc); {$I+}
err := IOResult;
dbUpdateHeader := NoError;
if not NoError then exit;
{$I-} blockwrite(dbfin,dbhead,sizeof(dbhead),numwritten); {$I+}
err := IOResult;
if(numwritten = 0) then err := -9;
dbUpdateHeader := NoError;
end;
Function XBASE_DBF_object.dbrewrite(rec_no:longint):boolean;
{ rewrites the dbbuf[] over the current record of the database, returns
STD_ERR_CODES.
}
var
numwritten :word;
fileloc :longint;
begin
err := 0;
dbrewrite := false;
if not opened or not writepermitted then
begin
err := -99;
exit;
end;
if dbposition(rec_no) then
begin
{$I-} blockwrite(dbfin,dbbuf,dbhead.rec_bytes,numwritten); {$I+}
err := IOResult;
dbrewrite := NoError;
end
else dbrewrite := false;
if NoError then
begin
dbSetHeaderDate;
if dbUpdateHeader then
begin
dbrewrite := dbgoto(rec_no);
end;
end;
dbrewrite := NoError;
end;
Function XBASE_DBF_object.dbdelete(rec_no:longint):boolean;
{ rewrites the dbbuf[] over the (rec_no) record of the database, returns
STD_ERR_CODES.
}
var
numwritten :word;
fileloc :longint;
begin
err := 0;
dbdelete := false;
if not opened or not writepermitted then
begin
err := -99;
exit;
end;
if dbposition(rec_no) then
begin
dbbuf[1] := '*'; { 2Ah }
{$I-} blockwrite(dbfin,dbbuf,dbhead.rec_bytes,numwritten); {$I+}
err := IOResult;
end
else dbdelete := false;
if NoError then
begin
dbSetHeaderDate;
if dbUpdateHeader then
begin
dbdelete := dbgoto(rec_no);
end;
end;
dbdelete := NoError;
end;
Function XBASE_DBF_object.dbappend : boolean;
{ appends the dbbuf[] record to the end of the database,
returns STD_ERR_CODES.}
var
numwritten :word;
fileloc :longint;
begin
err := 0;
dbappend := false;
if not opened or not writepermitted then
begin
err := -99;
exit;
end;
{$I-} seek(dbfin,FileSize(dbfin)); {$I+}
err := IOResult;
if not NoError then exit;
{$I-} blockwrite(dbfin,dbbuf,dbhead.rec_bytes,numwritten); {$I+}
err := IOResult;
if not NoError then exit;
if(numwritten = 0) then err := REWRITE_ERR;
inc(dbhead.no_rec);
dbSetHeaderDate;
if dbUpdateHeader then
begin
dbappend := dbbottom;
end;
dbappend := NoError;
end;
Procedure XBASE_DBF_object.dbFieldInfo(fldno:integer; var fldnam : string;
var rtype : char; var width,decp : byte);
begin
rtype := chr(0);
width := 0;
decp := 0;
fldnam := '';
if (fldno > 0) and (fldno <= no_col) then
begin
rtype := rstru[fldno].rtype;
width := rstru[fldno].width;
decp := rstru[fldno].decp;
fldnam := rstru[fldno].name;
end;
end;
Function XBASE_DBF_object.dbExportrec : string;
var s : string;
begin
s := '<dbExportRec not ready>';
dbExportrec := s;
end;
Function XBASE_DBF_object.dbExportDef : string;
var s : string;
begin
s := '<dbExportDef not ready>';
end;
{SECTION zzInitialization }
begin {initialization}
end.